"""With this file starts execution of main program."""

import os
import sys
from getopt import getopt

from app import logger, gui
from app.util import path

usage = """
Neo freerunner negotiator.

This program is used to control and perform widely-used tasks with
Neo Freerunner via fancy GUI interface.

Options:
    -h --help       display this help
    --config-dir    set path to non-standard config dir
    --log-dir       set path to non-standard logging dir
"""

programOptions = {
    # show help
    ("h", "help"):  "help",
    # set non-default config dir
    ("config-dir", ): "configDir",
    # set non-default logging dir
    ("log-dir", ): "loggingDir",
    # etc..
    ("bin-dir", ): "binaryDir",
    ("tmp-dir", ): "tmpDir",
}

def _defProgDir(name):
    return lambda opts: os.path.join(opts["programDir"], name)

defaultValues = {
    "configDir": _defProgDir("config"),
    "loggingDir": _defProgDir("log"),
    "binaryDir": _defProgDir("bin"),
    "tmpDir": _defProgDir("tmp"),
}

def translateOptions(opts):
    """Translate options to standard internal notation."""
    #
    # firstly, remove all leading '-'
    _cleanOpts = dict((_k.lstrip("-"), _v) for (_k, _v) in opts.iteritems())
    opts.clear()
    opts.update(_cleanOpts)
    # now perform actual translation
    _translatedOpts = {}
    for (_keys, _newKey) in programOptions.iteritems():
        _values = [opts[_key] for _key in _keys if opts.has_key(_key)]
        if _values:
            # at least one key was found!
            [opts.pop(_key, None) for _key in _keys]
            # if there are many values, we chose any of them
            _translatedOpts[_newKey] = _values[0]
    opts.clear()
    opts.update(_translatedOpts)

def setDefaultOptions(opts):
    """Set default values for non-given options."""
    for (_key, _value) in defaultValues.iteritems():
        if not opts.has_key(_key):
            opts[_key] = _value(opts)

def run(args):
    (_progOpts, _commArgs) = getopt(args, "h", ["config-dir=", "help"])
    assert not _commArgs
    _progOpts = dict(_progOpts)
    _programDir = path.getRootDir()
    translateOptions(_progOpts)
    _progOpts["programDir"] = _programDir
    setDefaultOptions(_progOpts)
    # option assembling finished.
    if _progOpts.has_key("help"):
        sys.stdout.write(usage)
        return
    _logger = logger.getLogger()
    _logger.addLoggingDir(_progOpts["loggingDir"])
    _logger.setSysHooks()
    from app import appRoot
    _app = appRoot.Application(_progOpts, _logger)
    try:
        _app.run()
    finally:
        _app.terminate()
    _logger.close()


def checkLibraries():
    """Check that all required libraries are installed

    and match version requirements.

    """

    def _printErrBanner(*msg):
        _line = "*" * 40
        sys.stdout.write("\n".join(("", "", _line) + msg + (_line, "", "")))

    def _getMajorMinor(data):
        """Major and minor as integer tuple from 'major.minor.<anything> string"""
        _rv = data.split(".", 2)[:2]
        _rv = map(lambda _el: _el.isdigit() and int(_el), _rv)
        if False in _rv:
            # parsing failed
            _rv = ()
        return tuple(_rv)

    def _printImportError(libName, libUrl):
        _printErrBanner("'%s' library import failed.\n" % libName,
            "Check that %s is installed." % libName,
            "(And if it isn't, download it " \
                "from %s )\n" % libUrl,
            "Error traceback follows.",
        )

    #### wxPython check
    try:
        import wx
    except:
        _printImportError("wxPython", "http://wxpython.org/download.php")
        raise
    _version = wx.version()
    # version has format "a.b.c.d (notes)"
    if not _getMajorMinor(_version) or _getMajorMinor(_version) < (2, 8):
        _printErrBanner("Unsupported wxPython version '%s'" % _version,
            "Please install wxPython >= 2.8")
        return False

    #### paramiko check
    try:
        import paramiko
    except:
        _printImportError("Paramiko", "http://www.lag.net/paramiko")
        raise
    _version = paramiko.__version__
    if not _getMajorMinor(_version) or _getMajorMinor(_version) < (1, 6):
        _printErrBanner("Unsupported paramiko version '%s'" % _version,
            "Please install paramiko >= 1.6")
        return False

    #### yaml check
    try:
        import yaml
    except:
        _printImportError("pyYaml", "http://pyyaml.org/")
        raise
    # seems that any version will do.

    #### PIL check
    try:
        import Image
    except:
        _printImportError("PIL", "http://www.pythonware.com/products/pil/")
        raise
    return True

if __name__ == "__main__":
    # do not check libararies when running in compiled mode
    if checkLibraries():
        run(sys.argv[1:])

# vim: set sts=4 sw=4 et :
